home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / J A V A / Java Development Kit V1.2 / jdk12-win32(1).exe / data1.cab / demos / demo / applets / GraphLayout / Graph.java < prev    next >
Encoding:
Java Source  |  1998-12-01  |  10.9 KB  |  424 lines

  1. /*
  2.  * @(#)Graph.java    1.7 98/07/17
  3.  *
  4.  * Copyright (c) 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
  7.  * modify and redistribute this software in source and binary code form,
  8.  * provided that i) this copyright notice and license appear on all copies of
  9.  * the software; and ii) Licensee does not utilize the software in a manner
  10.  * which is disparaging to Sun.
  11.  *
  12.  * This software is provided "AS IS," without a warranty of any kind. ALL
  13.  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
  14.  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
  15.  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
  16.  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
  17.  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
  18.  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
  19.  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
  20.  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
  21.  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
  22.  * POSSIBILITY OF SUCH DAMAGES.
  23.  *
  24.  * This software is not designed or intended for use in on-line control of
  25.  * aircraft, air traffic, aircraft navigation or aircraft communications; or in
  26.  * the design, construction, operation or maintenance of any nuclear
  27.  * facility. Licensee represents and warrants that it will not use or
  28.  * redistribute the Software for such purposes.
  29.  */
  30.  
  31. import java.util.*;
  32. import java.awt.*;
  33. import java.applet.Applet;
  34. import java.awt.event.*;
  35.  
  36.  
  37. class Node {
  38.     double x;
  39.     double y;
  40.  
  41.     double dx;
  42.     double dy;
  43.  
  44.     boolean fixed;
  45.  
  46.     String lbl;
  47. }
  48.  
  49.  
  50. class Edge {
  51.     int from;
  52.     int to;
  53.  
  54.     double len;
  55. }
  56.  
  57.  
  58. class GraphPanel extends Panel
  59.     implements Runnable, MouseListener, MouseMotionListener {
  60.     Graph graph;
  61.     int nnodes;
  62.     Node nodes[] = new Node[100];
  63.  
  64.     int nedges;
  65.     Edge edges[] = new Edge[200];
  66.  
  67.     Thread relaxer;
  68.     boolean stress;
  69.     boolean random;
  70.  
  71.     GraphPanel(Graph graph) {
  72.     this.graph = graph;
  73.     addMouseListener(this);
  74.     }
  75.  
  76.     int findNode(String lbl) {
  77.     for (int i = 0 ; i < nnodes ; i++) {
  78.         if (nodes[i].lbl.equals(lbl)) {
  79.         return i;
  80.         }
  81.     }
  82.     return addNode(lbl);
  83.     }
  84.     int addNode(String lbl) {
  85.     Node n = new Node();
  86.     n.x = 10 + 380*Math.random();
  87.     n.y = 10 + 380*Math.random();
  88.     n.lbl = lbl;
  89.     nodes[nnodes] = n;
  90.     return nnodes++;
  91.     }
  92.     void addEdge(String from, String to, int len) {
  93.     Edge e = new Edge();
  94.     e.from = findNode(from);
  95.     e.to = findNode(to);
  96.     e.len = len;
  97.     edges[nedges++] = e;
  98.     }
  99.  
  100.     public void run() {
  101.         Thread me = Thread.currentThread();
  102.     while (relaxer == me) {
  103.         relax();
  104.         if (random && (Math.random() < 0.03)) {
  105.         Node n = nodes[(int)(Math.random() * nnodes)];
  106.         if (!n.fixed) {
  107.             n.x += 100*Math.random() - 50;
  108.             n.y += 100*Math.random() - 50;
  109.         }
  110.         graph.play(graph.getCodeBase(), "audio/drip.au");
  111.         }
  112.         try {
  113.         Thread.sleep(100);
  114.         } catch (InterruptedException e) {
  115.         break;
  116.         }
  117.     }
  118.     }
  119.  
  120.     synchronized void relax() {
  121.     for (int i = 0 ; i < nedges ; i++) {
  122.         Edge e = edges[i];
  123.         double vx = nodes[e.to].x - nodes[e.from].x;
  124.         double vy = nodes[e.to].y - nodes[e.from].y;
  125.         double len = Math.sqrt(vx * vx + vy * vy);
  126.             len = (len == 0) ? .0001 : len;
  127.         double f = (edges[i].len - len) / (len * 3);
  128.         double dx = f * vx;
  129.         double dy = f * vy;
  130.  
  131.         nodes[e.to].dx += dx;
  132.         nodes[e.to].dy += dy;
  133.         nodes[e.from].dx += -dx;
  134.         nodes[e.from].dy += -dy;
  135.     }
  136.  
  137.     for (int i = 0 ; i < nnodes ; i++) {
  138.         Node n1 = nodes[i];
  139.         double dx = 0;
  140.         double dy = 0;
  141.  
  142.         for (int j = 0 ; j < nnodes ; j++) {
  143.         if (i == j) {
  144.             continue;
  145.         }
  146.         Node n2 = nodes[j];
  147.         double vx = n1.x - n2.x;
  148.         double vy = n1.y - n2.y;
  149.         double len = vx * vx + vy * vy;
  150.         if (len == 0) {
  151.             dx += Math.random();
  152.             dy += Math.random();
  153.         } else if (len < 100*100) {
  154.             dx += vx / len;
  155.             dy += vy / len;
  156.         }
  157.         }
  158.         double dlen = dx * dx + dy * dy;
  159.         if (dlen > 0) {
  160.         dlen = Math.sqrt(dlen) / 2;
  161.         n1.dx += dx / dlen;
  162.         n1.dy += dy / dlen;
  163.         }
  164.     }
  165.  
  166.     Dimension d = getSize();
  167.     for (int i = 0 ; i < nnodes ; i++) {
  168.         Node n = nodes[i];
  169.         if (!n.fixed) {
  170.         n.x += Math.max(-5, Math.min(5, n.dx));
  171.         n.y += Math.max(-5, Math.min(5, n.dy));
  172.             }
  173.             if (n.x < 0) {
  174.                 n.x = 0;
  175.             } else if (n.x > d.width) {
  176.                 n.x = d.width;
  177.             }
  178.             if (n.y < 0) {
  179.                 n.y = 0;
  180.             } else if (n.y > d.height) {
  181.                 n.y = d.height;
  182.             }
  183.         n.dx /= 2;
  184.         n.dy /= 2;
  185.     }
  186.     repaint();
  187.     }
  188.  
  189.     Node pick;
  190.     boolean pickfixed;
  191.     Image offscreen;
  192.     Dimension offscreensize;
  193.     Graphics offgraphics;
  194.  
  195.     final Color fixedColor = Color.red;
  196.     final Color selectColor = Color.pink;
  197.     final Color edgeColor = Color.black;
  198.     final Color nodeColor = new Color(250, 220, 100);
  199.     final Color stressColor = Color.darkGray;
  200.     final Color arcColor1 = Color.black;
  201.     final Color arcColor2 = Color.pink;
  202.     final Color arcColor3 = Color.red;
  203.  
  204.     public void paintNode(Graphics g, Node n, FontMetrics fm) {
  205.     int x = (int)n.x;
  206.     int y = (int)n.y;
  207.     g.setColor((n == pick) ? selectColor : (n.fixed ? fixedColor : nodeColor));
  208.     int w = fm.stringWidth(n.lbl) + 10;
  209.     int h = fm.getHeight() + 4;
  210.     g.fillRect(x - w/2, y - h / 2, w, h);
  211.     g.setColor(Color.black);
  212.     g.drawRect(x - w/2, y - h / 2, w-1, h-1);
  213.     g.drawString(n.lbl, x - (w-10)/2, (y - (h-4)/2) + fm.getAscent());
  214.     }
  215.  
  216.     public synchronized void update(Graphics g) {
  217.     Dimension d = getSize();
  218.     if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) {
  219.         offscreen = createImage(d.width, d.height);
  220.         offscreensize = d;
  221.         offgraphics = offscreen.getGraphics();
  222.         offgraphics.setFont(getFont());
  223.     }
  224.  
  225.     offgraphics.setColor(getBackground());
  226.     offgraphics.fillRect(0, 0, d.width, d.height);
  227.     for (int i = 0 ; i < nedges ; i++) {
  228.         Edge e = edges[i];
  229.         int x1 = (int)nodes[e.from].x;
  230.         int y1 = (int)nodes[e.from].y;
  231.         int x2 = (int)nodes[e.to].x;
  232.         int y2 = (int)nodes[e.to].y;
  233.         int len = (int)Math.abs(Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) - e.len);
  234.         offgraphics.setColor((len < 10) ? arcColor1 : (len < 20 ? arcColor2 : arcColor3)) ;
  235.         offgraphics.drawLine(x1, y1, x2, y2);
  236.         if (stress) {
  237.         String lbl = String.valueOf(len);
  238.         offgraphics.setColor(stressColor);
  239.         offgraphics.drawString(lbl, x1 + (x2-x1)/2, y1 + (y2-y1)/2);
  240.         offgraphics.setColor(edgeColor);
  241.         }
  242.     }
  243.  
  244.     FontMetrics fm = offgraphics.getFontMetrics();
  245.     for (int i = 0 ; i < nnodes ; i++) {
  246.         paintNode(offgraphics, nodes[i], fm);
  247.     }
  248.     g.drawImage(offscreen, 0, 0, null);
  249.     }
  250.  
  251.     //1.1 event handling
  252.     public void mouseClicked(MouseEvent e) {
  253.     }
  254.  
  255.     public void mousePressed(MouseEvent e) {
  256.         addMouseMotionListener(this);
  257.     double bestdist = Double.MAX_VALUE;
  258.     int x = e.getX();
  259.     int y = e.getY();
  260.     for (int i = 0 ; i < nnodes ; i++) {
  261.         Node n = nodes[i];
  262.         double dist = (n.x - x) * (n.x - x) + (n.y - y) * (n.y - y);
  263.         if (dist < bestdist) {
  264.         pick = n;
  265.         bestdist = dist;
  266.         }
  267.     }
  268.     pickfixed = pick.fixed;
  269.     pick.fixed = true;
  270.     pick.x = x;
  271.     pick.y = y;
  272.     repaint();
  273.     e.consume();
  274.     }
  275.  
  276.     public void mouseReleased(MouseEvent e) {
  277.         removeMouseMotionListener(this);
  278.     pick.x = e.getX();
  279.     pick.y = e.getY();
  280.     pick.fixed = pickfixed;
  281.     pick = null;
  282.     repaint();
  283.     e.consume();
  284.     }
  285.  
  286.     public void mouseEntered(MouseEvent e) {
  287.     }
  288.  
  289.     public void mouseExited(MouseEvent e) {
  290.     }
  291.  
  292.     public void mouseDragged(MouseEvent e) {
  293.     pick.x = e.getX();
  294.     pick.y = e.getY();
  295.     repaint();
  296.     e.consume();
  297.     }
  298.  
  299.     public void mouseMoved(MouseEvent e) {
  300.     }
  301.  
  302.     public void start() {
  303.     relaxer = new Thread(this);
  304.     relaxer.start();
  305.     }
  306.  
  307.     public void stop() {
  308.     relaxer = null;
  309.     }
  310.  
  311. }
  312.  
  313.  
  314. public class Graph extends Applet implements ActionListener, ItemListener {
  315.  
  316.     GraphPanel panel;
  317.     Panel controlPanel;
  318.  
  319.     Button scramble = new Button("Scramble");
  320.     Button shake = new Button("Shake");
  321.     Checkbox stress = new Checkbox("Stress");
  322.     Checkbox random = new Checkbox("Random");
  323.  
  324.     public void init() {
  325.     setLayout(new BorderLayout());
  326.  
  327.     panel = new GraphPanel(this);
  328.     add("Center", panel);
  329.     controlPanel = new Panel();
  330.     add("South", controlPanel);
  331.  
  332.     controlPanel.add(scramble); scramble.addActionListener(this);
  333.     controlPanel.add(shake); shake.addActionListener(this);
  334.     controlPanel.add(stress); stress.addItemListener(this);
  335.     controlPanel.add(random); random.addItemListener(this);
  336.  
  337.     String edges = getParameter("edges");
  338.     for (StringTokenizer t = new StringTokenizer(edges, ",") ; t.hasMoreTokens() ; ) {
  339.         String str = t.nextToken();
  340.         int i = str.indexOf('-');
  341.         if (i > 0) {
  342.         int len = 50;
  343.         int j = str.indexOf('/');
  344.         if (j > 0) {
  345.             len = Integer.valueOf(str.substring(j+1)).intValue();
  346.             str = str.substring(0, j);
  347.         }
  348.         panel.addEdge(str.substring(0,i), str.substring(i+1), len);
  349.         }
  350.     }
  351.     Dimension d = getSize();
  352.     String center = getParameter("center");
  353.     if (center != null){
  354.         Node n = panel.nodes[panel.findNode(center)];
  355.         n.x = d.width / 2;
  356.         n.y = d.height / 2;
  357.         n.fixed = true;
  358.     }
  359.     }
  360.  
  361.     public void destroy() {
  362.         remove(panel);
  363.         remove(controlPanel);
  364.     }
  365.  
  366.     public void start() {
  367.     panel.start();
  368.     }
  369.  
  370.     public void stop() {
  371.     panel.stop();
  372.     }
  373.  
  374.     public void actionPerformed(ActionEvent e) {
  375.     Object src = e.getSource();
  376.  
  377.     if (src == scramble) {
  378.         play(getCodeBase(), "audio/computer.au");
  379.         Dimension d = getSize();
  380.         for (int i = 0 ; i < panel.nnodes ; i++) {
  381.         Node n = panel.nodes[i];
  382.         if (!n.fixed) {
  383.             n.x = 10 + (d.width-20)*Math.random();
  384.             n.y = 10 + (d.height-20)*Math.random();
  385.         }
  386.         }
  387.         return;
  388.     }
  389.  
  390.     if (src == shake) {
  391.         play(getCodeBase(), "audio/gong.au");
  392.         Dimension d = getSize();
  393.         for (int i = 0 ; i < panel.nnodes ; i++) {
  394.         Node n = panel.nodes[i];
  395.         if (!n.fixed) {
  396.             n.x += 80*Math.random() - 40;
  397.             n.y += 80*Math.random() - 40;
  398.         }
  399.         }
  400.     }
  401.  
  402.     }
  403.  
  404.     public void itemStateChanged(ItemEvent e) {
  405.     Object src = e.getSource();
  406.     boolean on = e.getStateChange() == ItemEvent.SELECTED;
  407.     if (src == stress) panel.stress = on;
  408.     else if (src == random) panel.random = on;
  409.     }
  410.  
  411.     public String getAppletInfo() {
  412.     return "Title: GraphLayout \nAuthor: <unknown>";
  413.     }
  414.  
  415.     public String[][] getParameterInfo() {
  416.     String[][] info = {
  417.         {"edges", "delimited string", "A comma-delimited list of all the edges.  It takes the form of 'C-N1,C-N2,C-N3,C-NX,N1-N2/M12,N2-N3/M23,N3-NX/M3X,...' where C is the name of center node (see 'center' parameter) and NX is a node attached to the center node.  For the edges connecting nodes to eachother (and not to the center node) you may (optionally) specify a length MXY separated from the edge name by a forward slash."},
  418.         {"center", "string", "The name of the center node."}
  419.     };
  420.     return info;
  421.     }
  422.  
  423. }
  424.